//
// Created by alexander on 2/20/19.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include </home/alexander/CLionProjects/FFmpegLogTest/log/log.h>
#include "convertVideoToBmp.h"

#define INBUF_SIZE 4096

#define WORD uint16_t
#define DWORD uint32_t
#define LONG int32_t

int saveJpg(AVFrame *avFrame, char *fileName)
{
    int returnCode = -1;
    int width = avFrame->width;
    int height = avFrame->height;
    AVFormatContext *outputAVFormatContext = avformat_alloc_context();

    /**设置输出的文件编码格式*/
    AVOutputFormat *avOutputFormat = av_guess_format("mjpeg", NULL, NULL);
    outputAVFormatContext->oformat = avOutputFormat;

    returnCode = avio_open(&outputAVFormatContext->pb, fileName, AVIO_FLAG_READ_WRITE);
    if(returnCode < 0)
    {
        printf("Could not open output file\n");
        return -1;
    }
    AVStream *pAVStream = avformat_new_stream(outputAVFormatContext, NULL);
    if(pAVStream == NULL)
    {
        printf("Create output stream failed\n");
        return -1;
    }
    /**设置该stream的参数信息*/
    AVCodecContext *pCodecContext = pAVStream->codec;

    pCodecContext->codec_id = outputAVFormatContext->oformat->video_codec;
    pCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
    pCodecContext->pix_fmt = AV_PIX_FMT_YUVJ420P;
    pCodecContext->width = width;
    pCodecContext->height = height;
    pCodecContext->time_base.num = 1;
    pCodecContext->time_base.den = 25;
    av_dump_format(outputAVFormatContext, 0, fileName, 1);

    /**查找编码器，把avFrame编码成对应的图片格式*/
    AVCodec *pCodec = avcodec_find_encoder(pCodecContext->codec_id);
    if(!pCodec)
    {
        printf("Could not find codec\n");
        return -1;
    }
    /**设置pCodecContext的编码器为pCodec，并打开解码器*/
    returnCode = avcodec_open2(pCodecContext, pCodec, NULL);
    if(returnCode < 0)
    {
        printf("Could not open encoder codec\n");
        return -1;
    }

    returnCode = avformat_write_header(outputAVFormatContext, NULL);
    if(returnCode < 0)
    {
        printf("Write header failer\n");
        return -1;
    }
    /**实例化AVPAcket,并给予足够的空间*/
    AVPacket avPacket;
    av_new_packet(&avPacket, width * height * 3);

    int got_picture = 0;
    /**编码图片*/
    returnCode = avcodec_encode_video2(pCodecContext, &avPacket, avFrame, &got_picture);
    if(returnCode < 0)
    {
        printf("Encode error\n");
        return -1;
    }
    if(got_picture == 1)
    {
        printf("Get picture success\n");
        returnCode = av_interleaved_write_frame(outputAVFormatContext, &avPacket);
        if(returnCode < 0)
        {
            printf("Write frame failed\n");
        }
    }
    av_packet_unref(&avPacket);
    av_write_trailer(outputAVFormatContext);
    printf("Write picture success\n");

    if(pAVStream)
    {
        avcodec_close(pAVStream->codec);
    }
    avio_close(outputAVFormatContext->pb);
    avformat_free_context(outputAVFormatContext);
    return 0;

}

int decode_write_frame2(const char *outfilename, AVCodecContext *avCodecContext, AVFrame *frame,
                        int *frame_count, AVPacket *pkt, int last)
{
    int returnCode = 0;
    int len, got_frame;
    char buf[1024];

    /**把avPacket中的压缩编码数据解码到frame中*/
    len = avcodec_decode_video2(avCodecContext, frame, &got_frame, pkt);
    if(len < 0)
    {
        fprintf(stderr, "Error while decoding frame %d\n", *frame_count);
        return len;
    }

    if(got_frame)
    {
        printf("Saving %s frame %3d\n", last ? "last " : "", *frame_count);
        fflush(stdout);

        /**图片的内存由解码器释放，不用手动释放*/
        snprintf(buf, sizeof(buf), "%s---%d.jpg", outfilename, *frame_count);

        returnCode = saveJpg(frame, buf);

        (*frame_count)++;
    }
    return returnCode;
}

int convertVideoToJpg(const char *inputFileName, const char *outputFileName)
{
    int returnCode = -1;
    AVFormatContext *avFormatContext;
    const AVCodec *avCodec;
    AVCodecContext *avCodecContext = NULL;
    AVStream * avStream = NULL;
    int streamIndex;
    int frameCount;
    AVFrame *avFrame;
    AVPacket avPacket;

    av_register_all();

    /**打开输入文件，并为avFormatContext申请空间*/
    returnCode = avformat_open_input(&avFormatContext, inputFileName, NULL, NULL);
    if(returnCode < 0)
    {
        printf("Could not open source file %s\n", inputFileName);
        exit(1);
    }

    returnCode = avformat_find_stream_info(avFormatContext, NULL);
    if(returnCode < 0)
    {
        printf("Could not find stream information\n");
        exit(1);
    }

    /*输出输入文件信息*/
    av_dump_format(avFormatContext, 0, inputFileName, 0);

    av_init_packet(&avPacket);
    avPacket.data = NULL;
    avPacket.size = 0;

    /**从输入文件中找到视频流的下标*/
    returnCode = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if(returnCode < 0)
    {
        printf("Could not find %s stream in input file %s\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO), inputFileName);
        return returnCode;
    }
    streamIndex = returnCode;
    avStream = avFormatContext->streams[streamIndex];

    /**为输入流寻找合适的解码器*/
    avCodec = avcodec_find_decoder(avStream->codecpar->codec_id);
    if(!avCodec)
    {
        printf("Failed to find %s codec\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
        return AVERROR(EINVAL);
    }

    avCodecContext = avcodec_alloc_context3(NULL);
    if(!avCodecContext)
    {
        printf("Could not alllocate video codec context\n");
        exit(1);
    }

    /**把输入流的编码器参数赋值给输出流上下文*/
    returnCode = avcodec_parameters_to_context(avCodecContext, avStream->codecpar);
    if(returnCode < 0)
    {
        printf("Failed to copy %s codec parameters to decoder context\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO));
        return returnCode;
    }

    /**打开解码器*/
    returnCode = avcodec_open2(avCodecContext, avCodec, NULL);
    if(returnCode < 0)
    {
        printf("Could not open codec\n");
        exit(1);
    }

    avFrame = av_frame_alloc();
    if(!avFrame)
    {
        printf("Could not allocate video frame\n");
        exit(1);
    }

    frameCount = 0;
    while (av_read_frame(avFormatContext, &avPacket) >= 0)
    {
        if(avPacket.stream_index == streamIndex)
        {
            returnCode = decode_write_frame2(outputFileName, avCodecContext, avFrame, &frameCount, &avPacket, 0);
            if(returnCode < 0)
            {
                exit(1);
            }
        }
        av_packet_unref(&avPacket);
    }
    avformat_close_input(&avFormatContext);
    avcodec_free_context(&avCodecContext);
    av_frame_free(&avFrame);

    return 0;
}

